﻿using MathNet.Numerics.Integration;
using MathNet.Numerics.LinearAlgebra.Double;
using System;
using System.Collections.Generic;
using System.Linq;

namespace mathTest
{
    public class Equation
    {
        public int Point1;
        public int Point2;
        public double Sum;
    }

    class Film1
    {
        private Random rand;
        private int upPointer;
        private int downPointer;

        public Film1(int upPointer, int downPointer)
        {
            rand = new Random();
            FilmThickness = Enumerable.Range(1, 120).Select(i => rand.NextDouble() + 10).ToArray();
            Equations = new List<Equation>();
            this.upPointer = upPointer;
            this.downPointer = downPointer;
            AList = new List<double[]>();
            BList = new List<double>();
            PointsUsed = new bool[120];
        }

        public DenseMatrix AMatrix { get; set; }
        public DenseVector BVector { get; set; }
        public List<double[]> AList { get; set; }
        public List<double> BList { get; set; }
        public double[] FilmThickness { get; set; }
        public List<Equation> Equations { get; set; }
        public int UpPointer { get => upPointer; set => upPointer = value; }
        public int DownPointer { get => downPointer; set => downPointer = value; }
        public int[] UpPoints { get; set; }
        public int[] DownPoints { get; set; }
        public bool[] PointsUsed { get; set; }

        public void Run()
        {
            int span = 4;
            int i = 1;
            GetPoints(i, 0, 30);
            GenerateEquations();
            i += span;
            GetPoints(i, 30, 30);
            GenerateEquations();
            i += span;
            GetPoints(i, 30, 30);
            GenerateEquations();
            i += span;
            GetPoints(i, 0, 30);
            GenerateEquations();

            int j = rand.Next(0, 119);
            Equations[119] = new Equation() { Point1 = -1, Point2 = j, Sum = FilmThickness[j] };

            SolveMatrix();
        }

        public void PrintRawFilmThickness()
        {
            foreach (var item in FilmThickness)
            {
                Console.WriteLine(item);
            }
        }

        private void GenerateEquations()
        {
            for (int i = 0; i < UpPoints.Length; i++)
            {
                Equations.Add(new Equation() { Point1 = UpPoints[i] - 1, Point2 = DownPoints[i] - 1, Sum = FilmThickness[UpPoints[i]-1] + FilmThickness[DownPoints[i]-1]});
            }
        }

        public double[] SolveMatrix()
        {
            foreach (var e in Equations)
            {
                var A = new double[120];
                if (e.Point1 >= 0)
                    A[e.Point1] = 1;
                if (e.Point2 >= 0)
                    A[e.Point2] = 1;

                AList.Add(A);
                BList.Add(e.Sum);
            }

            PrintMatrix(List2TwoArr(AList), BList.ToArray());

            AMatrix = DenseMatrix.OfArray(List2TwoArr(AList));
            BVector = new DenseVector(BList.ToArray());

            var ans = AMatrix.QR().Solve(BVector).ToArray();

            for (int i = 0; i < ans.Length; i++)
            {
                Console.ForegroundColor = Math.Abs(ans[i] - FilmThickness[i]) < 0.00001 ? ConsoleColor.Black : ConsoleColor.DarkRed;
                Console.WriteLine($"{i+1}. result:{ans[i]} - answer:{FilmThickness[i]}");
            }
            return ans;
        }

        private double[,] List2TwoArr(List<double[]> aList)
        {
            double[,] ans = new double[aList.Count, aList[1].Length];

            for (int i = 0; i < aList.Count; i++)
            {
                for (int j = 0; j < aList[i].Length; j++)
                {
                    ans[i, j] = aList[i][j];
                }
            }

            return ans;
        }

        public void GetPoints(int firstIndex, int startIndex, int numberOfPoints)
        {
            var totalPoints = 120;
            var maxIndex = totalPoints - 1;
            var indexOffset = totalPoints / 2;
            var secondIndex = (firstIndex + indexOffset > maxIndex) ? (firstIndex + indexOffset - maxIndex) : (firstIndex + indexOffset);
            var loop = Enumerable.Range(1, totalPoints);

            //UpPoints = loop.Skip(firstIndex).Take(indexOffset).ToArray();
            //DownPoints = loop.Skip(secondIndex).Take(maxIndex - secondIndex + 1)
            //    .Concat(loop.Take(secondIndex + indexOffset - 1 > maxIndex ? secondIndex + indexOffset - 1 - maxIndex : 0))
            //    .Reverse().ToArray();

            UpPoints = loop.Skip(firstIndex + startIndex).Take(numberOfPoints).ToArray();
            DownPoints = loop.Skip(secondIndex)
                .Take(maxIndex - secondIndex + 1)
                .Concat(loop.Take(secondIndex + indexOffset - 1 > maxIndex ? secondIndex + indexOffset - 1 - maxIndex : 0))
                .Reverse().Skip(startIndex).Take(numberOfPoints).ToArray();

            Console.WriteLine($"UpPoints长度：{UpPoints.Length}");
            foreach (var item in UpPoints)
            {
                Console.Write(item + ", ");
            }
            Console.WriteLine("\n");

            Console.WriteLine($"DownPoints长度：{DownPoints.Length}");
            foreach (var item in DownPoints)
            {
                Console.Write(item + ", ");
            }
            Console.WriteLine("\n----------------------------------------------------------------\n");

        }

        public void PrintMatrix(double[,] A, double[] b)
        {
            for (int row = 0; row < A.GetLength(0); row++)
            {
                for (int column = 0; column < A.GetLength(1); column++)
                {
                    Console.Write($"{A[row, column]} ");
                }

                Console.Write($"  line{row + 1}={b[row]}  ");
                Console.WriteLine();
            }
        }
    }
}
